package com.chenwc.httpclient;

import com.chenwc.util.Utils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HttpClient 网络请求工具类
 *
 * @author chenwc
 * @date 2023/5/17 22:25
 */
public class RestMock {

    private final static Logger log = LoggerFactory.getLogger(RestMock.class);

    /**
     * Socket 超时时间
     */
    private final static Integer SocketTimeout = 20000;
    /**
     * 连接超时时间
     */
    private final static Integer ConnectTimeout = 20000;

    public static final CookieStore cookieStore = new BasicCookieStore();

    /**
     * 打印本地cookie
     */
    private static void printLocalCookie() {
        List<Cookie> cookies = cookieStore.getCookies();
        for (Cookie cookie : cookies) {
            log.debug("Local cookie: " + cookie);
        }
    }

    /**
     * 根据url获取host
     *
     * @param url url
     * @return host
     */
    public static String getHost(String url) {
        if (url == null || url.trim().equals("")) {
            return "";
        }
        String host = "";
        Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+");
        Matcher matcher = p.matcher(url);
        if (matcher.find()) {
            host = matcher.group();
        }
        return host;
    }

    /**
     * 发送delete请求
     *
     * @param urlStr    url
     * @param headerMap 请求头
     * @return 响应信息
     */
    public static String sendDelete(String urlStr, Map<String, String> headerMap) {

        log.info("url----------------> " + urlStr);
        String sResponse = null;
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        try {
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            HttpDelete httpDelete = new HttpDelete(urlStr);
            httpDelete.setConfig(getRequestConfig());
            //装入头信息
            if (null != headerMap && !headerMap.isEmpty()) {
                for (String key : headerMap.keySet()) {
                    httpDelete.addHeader(key, headerMap.get(key));
                }
            }
            // 执行提交
            HttpResponse response = httpClient.execute(httpDelete);
            sResponse = getResponseMessage(response);
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sResponse;
    }

    /**
     * 发起 post 请求，提交文件和表单
     * 文件表单格式：form-data; name="file"; filename="xxxxxxxxxx"
     * 表单格式：form-data; name="key1"
     * 单格式：form-data; name="key2"
     *
     * @param urlStr      http请求的地址
     * @param headerMap   头信息
     * @param formBodyMap 表单参数
     * @param file        文件
     * @return 响应信息
     */
    public static String sendPostByFileAndFormBody(String urlStr, Map<String, String> headerMap, Map<String, String> formBodyMap, File file) {

        log.info("url----------------> " + urlStr);
        String sResponse = null;
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        try {
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            HttpPost httpPost = getHttpPost(urlStr, headerMap);

            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            //请求的编码格式
            //builder.setCharset(StandardCharsets.UTF_8);
            //浏览器兼容模式
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            FileBody fileBody = new FileBody(file);
            builder.addPart("file", fileBody);
            //添加表单参数
            if (null != formBodyMap && !formBodyMap.isEmpty()) {
                for (String key : formBodyMap.keySet()) {
                    StringBody strBody = new StringBody(formBodyMap.get(key), ContentType.MULTIPART_FORM_DATA.withCharset(StandardCharsets.UTF_8));
                    builder.addPart(key, strBody);
                }
            }
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            // 执行提交
            HttpResponse response = httpClient.execute(httpPost);
            sResponse = getResponseMessage(response);
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sResponse;
    }

    /**
     * 发起 post 请求，提交表单
     * 表单格式：form-data; name="key1"
     * 单格式：form-data; name="key2"
     *
     * @param urlStr      http请求的地址
     * @param headerMap   头信息
     * @param formBodyMap 表单参数
     * @return 响应信息
     */
    public static String sendPostByFormBody(String urlStr, Map<String, String> headerMap, Map<String, String> formBodyMap) {

        log.info("url----------------> " + urlStr);
        String sResponse = null;
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        try {
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            HttpPost httpPost = getHttpPost(urlStr, headerMap);
            StringEntity entity = getFormBodyEntity(formBodyMap);
            if (null != entity) {
                httpPost.setEntity(entity);
            }
            // 执行提交
            HttpResponse response = httpClient.execute(httpPost);

            sResponse = getResponseMessage(response);
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sResponse;
    }

    /**
     * 发起 post 请求，带有请求体
     *
     * @param urlStr    http请求的地址
     * @param headerMap 头信息
     * @param body      请求体
     * @return 响应信息
     */
    public static String sendPostByBody(String urlStr, Map<String, String> headerMap, String body) {

        log.info("url----------------> " + urlStr);
        String sResponse = null;
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        try {
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            ;
            HttpPost httpPost = getHttpPost(urlStr, headerMap);
            if (null != body && !body.isEmpty()) {
                StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
                httpPost.setEntity(entity);
            }
            // 执行提交
            HttpResponse response = httpClient.execute(httpPost);
            sResponse = getResponseMessage(response);
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sResponse;
    }

    /**
     * 发起 post 请求
     *
     * @param urlStr    http请求的地址
     * @param headerMap 头信息
     * @return 响应信息
     */
    public static String sendPost(String urlStr, Map<String, String> headerMap) {
        return sendPostByBody(urlStr, headerMap, null);
    }

    /**
     * 获取HttpPost
     *
     * @param url    url
     * @param header 头信息
     * @return HttpPost
     */
    private static HttpPost getHttpPost(String url, Map<String, String> header) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(getRequestConfig());
        //装入头信息
        if (null != header && !header.isEmpty()) {
            for (String key : header.keySet()) {
                httpPost.addHeader(key, header.get(key));
            }
        }
        return httpPost;
    }

    /**
     * 使用get请求下载文件
     *
     * @param urlStr   url
     * @param filePath 文件保存路径
     */
    public static void downloadFileByGet(String urlStr, String filePath) {
        log.info("url----------------> " + urlStr);
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        InputStream inputStream = null;
        FileOutputStream fos = null;
        File fileExists = new File(filePath);
        try {
            if (!fileExists.exists()) {
                if (!Utils.mkdirParents(fileExists)) {
                    log.info("创建父级目录失败！");
                    return;
                }
                if (!fileExists.createNewFile()) {
                    log.info("创建文件: {} 失败！", filePath);
                    return;
                }
            }
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            HttpGet httpGet = getHttpGet(urlStr, new HashMap<>(), new HashMap<>());
            // 执行提交
            HttpResponse response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("-----------------状态码--------------");
            log.debug("---------------------->statusCode: " + statusCode);
            if (statusCode == 200) {
                // 得到实体
                HttpEntity entity = response.getEntity();
                inputStream = entity.getContent();
                byte[] getData = Utils.readInputStream(inputStream);
                File file = new File(filePath);
                fos = new FileOutputStream(file);
                fos.write(getData);
                log.info("下载文件成功！");
                log.info("文件保存到：{}", filePath);
                Utils.printObjectSize(file.length());
            }
            printLocalCookie();
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != fos) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发起 get 请求，带有请求参数
     *
     * @param urlStr       http请求的地址
     * @param headerMap    头信息
     * @param parameterMap 请求参数
     * @return 响应信息
     */
    public static String sendGetByParameter(String urlStr, Map<String, String> headerMap, Map<String, String> parameterMap) {

        log.info("url----------------> " + urlStr);
        String sResponse = null;
        long t1 = System.currentTimeMillis();
        CloseableHttpClient httpClient = null;
        try {
            // 创建HttpClient
            httpClient = getHttpClient(urlStr.contains("https"));
            HttpGet httpGet = getHttpGet(urlStr, headerMap, parameterMap);
            // 执行提交
            HttpResponse response = httpClient.execute(httpGet);

            sResponse = getResponseMessage(response);
            long t2 = System.currentTimeMillis();
            log.debug("----------请求总用时：{} 秒", ((t2 - t1) / 1000.0));
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (null != httpClient) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sResponse;
    }

    /**
     * 发起 get 请求
     *
     * @param urlStr    http请求的地址
     * @param headerMap 头信息
     * @return 响应信息
     */
    public static String sendGet(String urlStr, Map<String, String> headerMap) {
        return sendGetByParameter(urlStr, headerMap, new HashMap<>());
    }

    /**
     * 获取HttpGet
     *
     * @param url          url
     * @param header       头信息
     * @param parameterMap 请求参数
     * @return HttpGet
     */
    private static HttpGet getHttpGet(String url, Map<String, String> header, Map<String, String> parameterMap) {
        String paramerterString = generateGetParameter(parameterMap);
        if (null != parameterMap && !parameterMap.isEmpty()) {
            if (url.endsWith("?")) {
                url = url + paramerterString;
            } else {
                url = url + "?" + paramerterString;
            }
        }
        log.info("getUrl---------------->{}", url);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(getRequestConfig());
        //装入头信息
        if (null != header && !header.isEmpty()) {
            for (String key : header.keySet()) {
                httpGet.addHeader(key, header.get(key));
            }
        }
        return httpGet;
    }

    /**
     * 根据表单 map 转换成 StringEntity
     *
     * @param formBodyMap 表单 map
     * @return StringEntity
     */
    private static StringEntity getFormBodyEntity(Map<String, String> formBodyMap) {
        StringEntity entity = null;
        List<NameValuePair> kvList = new ArrayList<>();
        if (null != formBodyMap && !formBodyMap.isEmpty()) {
            for (String key : formBodyMap.keySet()) {
                kvList.add(new BasicNameValuePair(key, formBodyMap.get(key)));
            }
        }
        if (!kvList.isEmpty()) {
            // 包装成一个Entity对象
            entity = new UrlEncodedFormEntity(kvList, StandardCharsets.UTF_8);
        }
        return entity;
    }

    /**
     * 获取 http 响应信息
     *
     * @param response HttpResponse
     * @return 响应信息
     * @throws IOException IOException
     */
    private static String getResponseMessage(HttpResponse response) throws IOException {

        printLocalCookie();
        String sResponse = null;
        int statusCode = response.getStatusLine().getStatusCode();
        log.info("-----------------状态码--------------");
        log.info("---------------------->statusCode: " + statusCode);
        HttpEntity responseEntity = response.getEntity();
        //响应状态码200
        if (null != responseEntity) {
            // 将响应的内容转换成字符串
            sResponse = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            log.debug("sResponse：--------------->" + sResponse);
        } else {
            log.info("响应信息为空！");
        }
        return sResponse;
    }

    /**
     * 获取 CloseableHttpClient
     *
     * @param isDeceive 是否绕过SSL
     * @return CloseableHttpClient
     */
    private static CloseableHttpClient getHttpClient(boolean isDeceive) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        CloseableHttpClient httpClient;
        if (isDeceive) {
            // 信任所有
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).setSSLSocketFactory(sslsf).build();
        } else {
            httpClient = HttpClients.custom()
                    .setDefaultCookieStore(cookieStore)
                    .build();
        }
        return httpClient;
    }

    /**
     * 获取 http 请求配置
     *
     * @return http 请求配置
     */
    private static RequestConfig getRequestConfig() {

        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        //设置超时时间，这个是httpclient 4.3版本之后的设置方法
        requestConfigBuilder.setConnectTimeout(ConnectTimeout);
        requestConfigBuilder.setSocketTimeout(SocketTimeout);

        return requestConfigBuilder.build();
    }

    /**
     * 根据map生成get请求参数，格式 A=a&B=b
     *
     * @param parameterMap 请求参数map
     * @return get请求参数url
     */
    public static String generateGetParameter(Map<String, String> parameterMap) {
        StringBuilder sb = new StringBuilder();
        if (null != parameterMap) {
            Set<String> keySet = parameterMap.keySet();
            int index = 1;
            for (String key : keySet) {
                String value = null;
                try {
                    value = URLEncoder.encode(parameterMap.get(key), String.valueOf(StandardCharsets.UTF_8));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                if (index == keySet.size()) {
                    sb.append(key).append("=").append(value);
                } else {
                    sb.append(key).append("=").append(value).append("&");
                }
                index++;
            }
        }
        return sb.toString();
    }
}